using System.IO;
using System.Runtime.InteropServices;


namespace System {
  public abstract unsafe class ProcessStream : Stream {
    private const int ProcessAllAccess = 0x1F0FFF;
    protected bool m_Open;
    protected int m_Position;
    protected IntPtr m_Process;
    public abstract IntPtr ProcessID { get; }
    public override bool CanRead {
      get { return true; }
    }
    public override bool CanWrite {
      get { return true; }
    }
    public override bool CanSeek {
      get { return true; }
    }

    public override long Length {
      get { throw new NotSupportedException(); }
    }
    public override long Position {
      get { return this.m_Position; }
      set { this.m_Position = (int)value; }
    }

    [DllImport( "Kernel32" )]
    private static extern IntPtr OpenProcess( int desiredAccess, int inheritHandle, IntPtr processID );

    [DllImport( "Kernel32" )]
    private static extern int CloseHandle( IntPtr handle );

    [DllImport( "Kernel32" )]
    private static extern int ReadProcessMemory( IntPtr process, int baseAddress, void* buffer, int size, ref int op );

    [DllImport( "Kernel32" )]
    private static extern int WriteProcessMemory( IntPtr process, int baseAddress, void* buffer, int size, int nullMe );

    public virtual bool BeginAccess() {
      if ( this.m_Open )
        return false;

      this.m_Process = OpenProcess( ProcessAllAccess, 0, ProcessID );
      this.m_Open = true;

      return true;
    }

    public virtual void EndAccess() {
      if ( !this.m_Open )
        return;

      CloseHandle( this.m_Process );
      this.m_Open = false;
    }

    public override void Flush() {
    }

    public override int Read( byte[] buffer, int offset, int count ) {
      bool end = !BeginAccess();

      int res = 0;

      fixed ( byte* p = buffer ) {
        ReadProcessMemory( this.m_Process, this.m_Position, p + offset, count, ref res );
      }

      this.m_Position += count;

      if ( end )
        EndAccess();

      return res;
    }

    public override void Write( byte[] buffer, int offset, int count ) {
      bool end = !BeginAccess();

      fixed ( byte* p = buffer ) {
        WriteProcessMemory( this.m_Process, this.m_Position, p + offset, count, 0 );
      }

      this.m_Position += count;

      if ( end )
        EndAccess();
    }

    public override void SetLength( long value ) {
      throw new NotSupportedException();
    }

    public override long Seek( long offset, SeekOrigin origin ) {
      switch ( origin ) {
        case SeekOrigin.Begin:
          this.m_Position = (int)offset;
          break;
        case SeekOrigin.Current:
          this.m_Position += (int)offset;
          break;
        case SeekOrigin.End:
          throw new NotSupportedException();
      }

      return this.m_Position;
    }
  }
}